NOTA: si consiglia di eseguire una riga alla volta di questo notebook, come fatto a lezione, cercando di capire sia cosa fa ciascuna funzione, sia soprattutto cercando di capire gli eventuali messaggi di errore.

Funzioni Scalari

Supponiamo ora di dover scrivere una funzione scalare:

$$ f : \mathbb{R}^n \rightarrow \mathbb{R}$$

Usiamo una lista di numeri per scrivere un vettore di $\mathbb{R}^n$, e scriviamo le seguenti funzioni:

  1. $\quad f(\vec{x}) = n$

  2. $\quad f(\vec{x}) = \sum_{i \in \{1,\dots,n\}} x_i$

  3. $\quad f(\vec{x}) = \sum_{i \in \{1,\dots,n\}} x_i^2$

  4. $\quad f(\vec{x}) = \min_{i\in \{1,\dots,n\}} x_i$

  5. $\quad f(\vec{x}) = \sum_{i \in \{1,\dots,n\}} g(x_i)$, dove $g : \mathbb{R} \rightarrow \mathbb{R}$

Costruiamo prima un vettore di numeri interi pseudo casuali, con distribuzione uniforme in [1,10].


In [ ]:
from random import randint
help(randint)

In [ ]:
As = [randint(1,10) for _ in range(10)]

In [ ]:
print(As)

È possibile inizializzare un generatore di numeri pseudo casuali con la funzione seed della libreria random.


In [ ]:
from random import seed
for _ in range(10):
    seed(13)
    print(randint(1,10))

Ora che abbiamo definito una lista di numeri casuali, veniamo ora alla prima delle funzioni richieste sopra.


In [ ]:
# La prima funzione la chiamiamo `LunghezzaLista`
def LunghezzaLista(Xs):
    """
    Calcola e restituisce il numero di elementi nella lista Xs.
    """
    # Itera la lista e conta il numero di suoi elementi
    n = 0
    for x in Xs:
        n += 1
    return n

# Esempio di esecuzione
print(As)
LunghezzaLista(As)

NOTA: Python ha un sistema di tipi dinamico. Cosa succede quindi se utilizzo una funzione passandogli dei parametri non previsti?


In [ ]:
LunghezzaLista(3)

In [ ]:
# La prima funzione la chiamiamo `LunghezzaLista`
def LunghezzaLista(Xs):
    """
    Calcola e restituisce il numero di elementi nella lista Xs.
    """
    # Controlla il tipo dell'argomento
    if type(Xs) != list:
        raise TypeError('Ueh, pirla! LunghezzaLista(Xs): La variabile di input Xs non e` una lista.')
        
    # Itera la lista e conta il numero di suoi elementi
    n = 0
    for x in Xs:
        n += 1
    return n

In [ ]:
LunghezzaLista(3)

IMPORTANTE: Determinare la lunghezza di una lista ha talmente importanza che esiste la funzione builtin len()


In [ ]:
len(As)

In [ ]:
help(list)

In [ ]:
# La seconda funzione la chiamiamo `SommaComponenti`
def SommaComponenti(Xs):
    """
    Calcola e restituisce il somma degli elementi nella lista Xs.
    """
    # Controlla il tipo dell'argomento
    if type(Xs) != list:
        raise TypeError('SommaComponenti(Xs): La variabile di input Xs non e` una lista.')
        
    # Itera la lista e conta il numero di suoi elementi
    somma = 0
    for x in Xs:
        somma = somma + x  # Oppure: somma += x
    return somma

In [ ]:
SommaComponenti(As)

In [ ]:
# La terza funzione la chiamiamo `SommaQuadrati`
def SommaQuadrati(Xs):
    """
    Calcola e restituisce il somma delle potenze quadrate dei singoli elementi nella lista Xs.
    """
    # Controlla il tipo dell'argomento
    if type(Xs) != list:
        raise TypeError('SommaComponenti(Xs): La variabile di input Xs non e` una lista.')
        
    # Itera la lista e conta il numero di suoi elementi
    somma = 0
    for x in Xs:
        somma += x**2
    return somma

In [ ]:
SommaQuadrati(As)

In [ ]:
# La quinta funzione la chiamiamo ElementoMinimo
def ElementoMinimo(Xs):
    """
    Trova l'elemento di valore minimo nella lista Xs
    """
    minimo = Xs[0]
    for x in Xs:
        if x < minimo:
            minimo = x
    return minimo

print(As)
print("minimo:", ElementoMinimo(As))
As.append(-3)
print(As)
print("minimo:", ElementoMinimo(As))

In [ ]:
# La quarta funzione la chiamiamo `SommaFunzioni`
def SommaFunzioni(Xs, G=lambda x: x**2):
    """
    Calcola e restituisce il somma delle funzioni G(x) applicata ai singoli elementi nella lista Xs.
    """
    # Controlla il tipo dell'argomento
    if type(Xs) != list:
        raise TypeError('SommaComponenti(Xs): La prima variabile di input Xs non e` una lista.')
        
    # Itera la lista e somma G(x) applicata ai suoi elementi
    somma = 0
    for x in Xs:
        somma += G(x)
    return somma

In [ ]:
SommaFunzioni(As)

In [ ]:
def G(x):
    return x**3

In [ ]:
SommaFunzioni(As, G)

In [ ]:
import math
SommaFunzioni(As, math.sin)

Altri esempi di funzioni

Supponiamo ora di voler scrivere funzioni vettoriali del tipo:

$$f : \mathbb{R}^n \rightarrow \mathbb{R}^m$$

come ad esempio:

  1. $f(x) = \left(0, \dots, x-1 \right)$, con $x \in \mathbb{Z}, x \geq 0$

  2. $f(\vec{x}) = \left(\min_{i\in \{1,\dots,n\}} x_i, \max_{i\in \{1,\dots,n\}} x_i\right)$

  3. $f(\vec{x}) = \left(x_1^3, \dots, x_n^3\right)$, con $x \in \mathbb{R}^n$


In [ ]:
# La prima funzione la chiamiamo Range
def Range(n):
    """
    Construisce e ritorna la lista (0, ..., n-1)
    """
    Ls = []
    i = 0
    while i < n:
        Ls.append(i)
        i += 1
    return Ls

print("Esempio di chiamata: Range(4) =", Range(4))

NOTA: Anche in questo caso esiste una funzione built-in di python chiamata range(). Leggere con l'help in linea la documentazione di range().


In [ ]:
# help(range)

In [ ]:
Ls = range(-10,10,2)
print(Ls)

In Python 3.x la creazione di liste viene eseguita in maniera lazy, ovvero la lista non viene effettivamente creata sino a quando non si itera sui suoi elementi. Questo può essere fatto in due modi:


In [ ]:
[i for i in Ls]

In [ ]:
list(Ls)

In [ ]:
# La seconda funzione la chiamiamo MinMaxElement
def MinMaxElement(Xs):
    minimo = Xs[0]
    massimo = minimo
    for x in Xs:
        if x < minimo:
            minimo = x
        if x > massimo:
            massimo = x
    return [minimo, massimo]

print("As =",As)
print("Esempio di chiamata: MinMaxElement(As) =", MinMaxElement(As))
r = MinMaxElement(As)
print("minimo =", r[0], "| massimo =", r[1])

In [ ]:
def Potenza(base, esponente=2):
    """
    Calcola la potenza (base)^(esponente).
    Se il secondo parametro non viene passato,
    calcola la potenza quadrata, ovvero (esponente=2).
    """
    
    return base**esponente

In [ ]:
# La terza funziona la chiamiamo semplicemene H,
# ed usiamo la funzione Potenza(base, esponente) per calcolare il cubo
def H(Xs):
    Ls = []
    for x in Xs:
        Ls += [Potenza(x, 3)]
    return Ls

print("Esempio di chiamata: H([2,3,1]) =", H([2,3,1]))

NOTA: L'operatore di addizione tra liste, aggiunge tutti gli elementi della seconda lista alla prima lista. Esempio:


In [ ]:
[3,2,6]+[3,1,9]

Overloading di un operatore

Un operatore può assumere un significativo diverso in base agli operandi a cui viene applicato. Per esempio:

L'operatore + :

  1. tra interi, floats, e numeri complessi esegue l'addizione
  2. tra liste, effettua la concatenazione

L'operatore * :

  1. tra interi, floats, e numeri complessi esegue la moltiplicazione
  2. tra un intero e una stringa: PROVARE (viene chiamato operatore di "repetition")

In [ ]:
" coding is fun! "*3

Rappresentazione grafica di funzioni scalari

Con i notebook di python si possono facilmente rappresentare graficamente le funzioni scalari.

Per poterlo fare si utilizza la libreria Matplotlib, che si ispira al plot di Matlab.

Per potere usare quella libreria la si deve "importare" con il comando seguente:


In [ ]:
from matplotlib.pyplot import *
%matplotlib inline

In [ ]:
%matplotlib inline
import matplotlib.pyplot as pyplot
# Per disegnare un singolo punto
pyplot.plot([1],[1], 'ro')

In [ ]:
# Per disegnare un singolo punto
pyplot.plot([1,2],[2,1], 'b')
pyplot.plot([1],[2], 'ro')
pyplot.plot([2],[1], 'ro')

In [ ]:
Xs = range(-100,100)
Ys = list(map(lambda x: math.sin(0.1*x), Xs))
pyplot.plot(Xs, Ys, 'r')

A questo punto possiamo rappresentare la funzione $f(x)=x^3$ nel modo seguente:


In [ ]:
#%matplotlib inline # Da usare sono una volta
X = range(-10, 11)
Y = H(X)
plot(X,Y)
title("Esempio di $f(x)=x^3$, con $-10 \leq x \leq 10$")

Importando la libreria math si hanno a disposizione tutta una serie di funzioni standard, tra cui: pow(x,y), sin(x), cos(x), tan(x), exp(x), log(x, base) che possono essere disegnate come segue:


In [ ]:
import math
X = [0.05*x for x in range(-62,62)]
Y = [math.sin(x) for x in X]
plot(X, Y)

Altri tipi di plot utili

Per maggiori informazioni guardare le demo di Matplotlib.


In [7]:
# ESEMPIO PRESO DA: http://matplotlib.org/examples/statistics/histogram_demo_cumulative.html
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import mlab

np.random.seed(0)
mu = 200
sigma = 25
n_bins = 50
x = np.random.normal(mu, sigma, size=100)

fig, ax = plt.subplots(figsize=(8, 4))

# plot the cumulative histogram
n, bins, patches = ax.hist(x, n_bins, normed=1, histtype='step',
                           cumulative=True, label='Empirica')

# Add a line showing the expected distribution.
y = mlab.normpdf(bins, mu, sigma).cumsum()
y /= y[-1]

ax.plot(bins, y, 'k--', linewidth=1.5, label='Teorica')

# tidy up the figure
ax.grid(True)
ax.legend(loc='right')
ax.set_title('Cumulative step histograms')
ax.set_xlabel('Annual rainfall (mm)')
ax.set_ylabel('Likelihood of occurrence')

plt.show()



In [8]:
# ESEMPIO PRESO DA: http://matplotlib.org/examples/statistics/histogram_demo_features.html
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

np.random.seed(0)

# example data
mu = 100  # mean of distribution
sigma = 15  # standard deviation of distribution
x = mu + sigma * np.random.randn(437)

num_bins = 50

fig, ax = plt.subplots()

# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, normed=1)

# add a 'best fit' line
y = mlab.normpdf(bins, mu, sigma)
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')

# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()



In [ ]: